home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / IFC_112 / netscape / application / Button.java < prev    next >
Encoding:
Text File  |  1999-05-28  |  53.8 KB  |  1,702 lines  |  [TEXT/CWIE]

  1. // Button.java
  2. // By Ned Etcode
  3. // Copyright 1995, 1996, 1997 Netscape Communications Corp.  All rights reserved.
  4.  
  5. package netscape.application;
  6.  
  7. import netscape.util.*;
  8.  
  9. /** View subclass that implementing a "button" control, an object with two
  10.   * states: "on" and "off."  The user changes a Button's state by clicking
  11.   * it, which may also cause the Button to draw itself differently.  For
  12.   * example, when clicked most Buttons draw themselves to appear "pressed" in.
  13.   * The Button class supports several types of Button, from PUSH_TYPE to
  14.   * TOGGLE_TYPE to RADIO_TYPE to CONTINUOUS_TYPE.  See the <b>setType()</b>
  15.   * method documentation for more information.
  16.   * @see #setType
  17.   * @note 1.0 removed implementation of Component interface
  18.   * @note 1.0 Changes for Keyboard UI
  19.   * @note 1.0 double click() radio button will no longer deselect them
  20.   * @note 1.0 archiving raised/lowered bezel
  21.   * @note 1.0 mouseDown now calls containsPoint() before processing Event
  22.   * @note 1.0 setHighlighted() and isHighlighted() now protected methods
  23.   * @note 1.0 Added FormElement interface for browser needs
  24.   */
  25. public class Button extends View implements Target, DrawingSequenceOwner,
  26.                                                 FormElement {
  27.     Font                _titleFont;
  28.     Color               _titleColor, _disabledTitleColor, _raisedColor,
  29.                         _loweredColor;
  30.     Image               _image, _altImage;
  31.     Border              _raisedBorder, _loweredBorder;
  32.     Sound               _downSound, _upSound;
  33.     Timer               _actionTimer;
  34.     Target              _target;
  35.     String              _title = "", _command, _altTitle = "";
  36.     int                 _type, _imagePosition, _repeatDelay = 75;
  37.     boolean             _state, _enabled = true, _bordered = true,
  38.                         _highlighted, _oldState, transparent = false;
  39.     private int         _clickCount;
  40.     private boolean     _performingAction;
  41.  
  42.     static Vector    _fieldDescription;
  43.  
  44.     /** Push button type. */
  45.     public final static int     PUSH_TYPE = 0;
  46.     /** Toggle button type. */
  47.     public final static int     TOGGLE_TYPE = 1;
  48.     /** Radio button type. */
  49.     public final static int     RADIO_TYPE = 2;
  50.     /** Continuous button type. */
  51.     public final static int     CONTINUOUS_TYPE = 3;
  52.  
  53.     /** Position Image to the title's left. */
  54.     public final static int     IMAGE_ON_LEFT = 0;
  55.     /** Position Image to the title's right. */
  56.     public final static int     IMAGE_ON_RIGHT = 1;
  57.     /** Position Image above the title. */
  58.     public final static int     IMAGE_ABOVE = 2;
  59.     /** Position Image below the title. */
  60.     public final static int     IMAGE_BELOW = 3;
  61.     /** Draw title on top of Image. */
  62.     public final static int     IMAGE_BENEATH = 4;
  63.  
  64.     /** Command causing the Button to send its command to its Target. */
  65.     public final static String SEND_COMMAND = "sendCommand";
  66.  
  67.     /** Command causing the Button to visually "click," as if pressed, as well
  68.       * as send its command to its Target.
  69.       */
  70.     public final static String CLICK = "click";
  71.  
  72.     /** Command causing a radio button to select the next button.
  73.       *
  74.       */
  75.     public final static String SELECT_NEXT_RADIO_BUTTON = "selectNextRadioButton";
  76.  
  77.     /** Command causing a radio button to select the previous button
  78.       *
  79.       */
  80.     public final static String SELECT_PREVIOUS_RADIO_BUTTON = "selectPreviousRadioButton";
  81.  
  82.     // Codable information
  83.  
  84.     final static String TITLE_KEY = "title";
  85.     final static String ALT_TITLE_KEY = "altTitle";
  86.     final static String TITLE_FONT_KEY = "titleFont";
  87.     final static String TITLE_COLOR_KEY = "titleColor";
  88.     final static String DISABLED_TITLE_COLOR_KEY = "disabledTitleColor";
  89.     final static String RAISED_COLOR_KEY = "raisedColor";
  90.     final static String LOWERED_COLOR_KEY = "loweredColor";
  91.     final static String IMAGE_KEY = "image";
  92.     final static String ALT_IMAGE_KEY = "altImage";
  93.     final static String DOWN_SOUND_KEY = "downSound";
  94.     final static String UP_SOUND_KEY = "upSound";
  95.     final static String TARGET_KEY = "target";
  96.     final static String COMMAND_KEY = "command";
  97.     final static String TYPE_KEY = "type";
  98.     final static String IMAGE_POSITION_KEY = "imagePosition";
  99.     final static String REPEAT_DELAY_KEY = "repeatDelay";
  100.     final static String STATE_KEY = "state";
  101.     final static String ENABLED_KEY = "enabled";
  102.     final static String BORDERED_KEY = "bordered";
  103.     final static String TRANSPARENT_KEY = "transparent";
  104.  
  105.     final static String RAISED_BORDER_KEY = "raisedBorder";
  106.     final static String LOWERED_BORDER_KEY = "loweredBorder";
  107.  
  108.  
  109. /* static methods */
  110.  
  111.     /** Returns a new Button instance configured to look and behave like a
  112.       * "push" button, a Button of type PUSH_TYPE.
  113.       */
  114.     public static Button createPushButton(int x, int y,
  115.                                           int width, int height) {
  116.         Button  pushButton;
  117.  
  118.         pushButton = new Button(x, y, width, height);
  119.         pushButton.setType(Button.PUSH_TYPE);
  120.  
  121.         return pushButton;
  122.     }
  123.  
  124.     /** Returns a new Button instance configured to look and behave like a
  125.       * "check" button, a Button of type TOGGLE_TYPE that displays a check mark
  126.       * when on.
  127.       */
  128.     public static Button createCheckButton(int x, int y,
  129.                                            int width, int height) {
  130.         Button  checkButton;
  131.  
  132.         checkButton = new Button(x, y, width, height);
  133.  
  134.         checkButton.setType(Button.TOGGLE_TYPE);
  135.         checkButton.setTransparent(true);
  136.  
  137.         checkButton.setImage(new CheckButtonImage(false));
  138.         checkButton.setAltImage(new CheckButtonImage(true));
  139.         checkButton.setImagePosition(Button.IMAGE_ON_LEFT);
  140.  
  141.         return checkButton;
  142.     }
  143.  
  144.     /** Returns a new Button instance configured to look and behave like a
  145.       * "radio" button, a Button of type RADIO_TYPE that can only be on if the
  146.       * others in its group are off.
  147.       */
  148.     public static Button createRadioButton(int x, int y,
  149.                                            int width, int height) {
  150.         Button  radioButton;
  151.  
  152.         radioButton = new Button(x, y, width, height);
  153.  
  154.         radioButton.setType(Button.RADIO_TYPE);
  155.         radioButton.setImage(Bitmap.bitmapNamed(
  156.                             "netscape/application/RadioButtonOff.gif"));
  157.         radioButton.setAltImage(Bitmap.bitmapNamed(
  158.                             "netscape/application/RadioButtonOn.gif"));
  159.         radioButton.setImagePosition(Button.IMAGE_ON_LEFT);
  160.         radioButton.setTransparent(true);
  161.  
  162.         return radioButton;
  163.     }
  164.  
  165.     /* constructors */
  166.  
  167.     /** Constructs a Button with origin (0, 0) and zero width and height.
  168.       */
  169.     public Button() {
  170.         this(0, 0, 0, 0);
  171.     }
  172.  
  173.     /** Constructs a Button with bounds <B>rect</B>.
  174.       */
  175.     public Button(Rect rect) {
  176.         this(rect.x, rect.y, rect.width, rect.height);
  177.     }
  178.  
  179.     /** Constructs a Button with bounds
  180.       * (<B>x</B>, <B>y</B>, <B>width</B>, <B>height</B>).
  181.       */
  182.     public Button(int x, int y, int width, int height) {
  183.         super(x, y, width, height);
  184.  
  185.         _titleColor = Color.black;
  186.         _disabledTitleColor = Color.gray;
  187.         _titleFont = Font.defaultFont();
  188.         _raisedBorder = BezelBorder.raisedButtonBezel();
  189.         _loweredBorder = BezelBorder.loweredButtonBezel();
  190.         _raisedColor = Color.lightGray;
  191.         _loweredColor = Color.lightGray;
  192.  
  193.         _setupKeyboard();
  194.     }
  195.  
  196.     /* attributes */
  197.  
  198.     /** Sets the Button's title and then calls the Button's
  199.       * <B>draw()</B> method to redraw it.  If <b>aString</b> contains
  200.       * newlines, the title will break and wrap to the next line at those
  201.       * points.  If you don't want to immediately
  202.       * redraw the Button, you should first call its
  203.       * <B>disableDrawing()</B> method.
  204.       * @see View#disableDrawing
  205.       */
  206.     public void setTitle(String aString) {
  207.         if (aString == null) {
  208.             _title = "";
  209.         } else {
  210.             _title = aString;
  211.         }
  212.         draw();
  213.     }
  214.  
  215.     /** Returns the Button's title.
  216.       * @see #setTitle
  217.       */
  218.     public String title() {
  219.         return _title;
  220.     }
  221.  
  222.     /** Sets the Button's alternate title, the title the Button displays
  223.       * when it's in its alternate state, and then calls the Button's
  224.       * <B>draw()</B> method to redraw it.  If <b>aString</b> contains
  225.       * newlines, the title will break and wrap to the next line at those
  226.       * points.  If you don't want to immediately redraw the Button, you
  227.       * should first call its <B>disableDrawing()</B> method.
  228.       * @see #setTitle
  229.       * @see View#disableDrawing
  230.       */
  231.     public void setAltTitle(String aString) {
  232.         if (aString == null) {
  233.             _altTitle = "";
  234.         } else {
  235.             _altTitle = aString;
  236.         }
  237.         setDirty(true);
  238.     }
  239.  
  240.     /** Returns the Button's alternate title.
  241.       * @see #setAltTitle
  242.       */
  243.     public String altTitle() {
  244.         return _altTitle;
  245.     }
  246.  
  247.     /** Sets the Button's "enabled" state and then calls the Button's
  248.       * <b>draw()</b> method to redraw it.  A disabled Button does not
  249.       * react to mouse clicks.  If you do not want to immediately
  250.       * redraw the Button, you should first call its
  251.       * <B>disableDrawing()</B> method.
  252.       * @see View#disableDrawing
  253.       */
  254.     public void setEnabled(boolean enabled) {
  255.         if (enabled != _enabled) {
  256.             _enabled = enabled;
  257.  
  258.             setDirty(true);
  259.         }
  260.     }
  261.  
  262.     /** Returns <b>true</b> if the Button is enabled.
  263.       * @see #setEnabled
  264.       */
  265.     public boolean isEnabled() {
  266.         return _enabled;
  267.     }
  268.  
  269.     void _setState(boolean newState) {
  270.         if (newState != _state) {
  271.             _state = newState;
  272.             draw();
  273. //            setDirty(true);
  274.         }
  275.     }
  276.  
  277.     void selectNextRadioButton(boolean forward) {
  278.         View sv = superview();
  279.         View peer;
  280.  
  281.         if(sv != null) {
  282.             Vector peers = sv.subviews();
  283.             int index = peers.indexOfIdentical(this);
  284.             int count = peers.count();
  285.             do {
  286.                 if(forward){
  287.                     index++;
  288.                     if(index == count)
  289.                         index = 0;
  290.                 } else {
  291.                     index--;
  292.                     if(index<0)
  293.                         index = count-1;
  294.                 }
  295.  
  296.                 peer = (View)peers.elementAt(index);
  297.                 if(peer instanceof Button && ((Button)peer).type() == RADIO_TYPE) {
  298.                     ((Button)peer).setState(true);
  299.                     rootView().selectView(peer,true);
  300.                     break;
  301.                 }
  302.             } while(peer != this);
  303.         }
  304.     }
  305.  
  306.     Button _otherActive() {
  307.         Button          nextPeer;
  308.         View            nextView;
  309.         Vector          peers;
  310.         int             i;
  311.  
  312.         if (superview() == null) {
  313.             return null;
  314.         }
  315.  
  316.         peers = superview().peersForSubview(this);
  317.         i = peers.count();
  318.         while (i-- > 0) {
  319.             nextView = (View)peers.elementAt(i);
  320.  
  321.             if (!(nextView instanceof Button) || nextView == this) {
  322.                 continue;
  323.             }
  324.  
  325.             nextPeer = (Button)nextView;
  326.             if (nextPeer.type() != RADIO_TYPE) {
  327.                 continue;
  328.             } else if (nextPeer.isEnabled() && nextPeer.state()) {
  329.                 return nextPeer;
  330.             }
  331.         }
  332.  
  333.         return null;
  334.     }
  335.  
  336.     /** Sets the Button's state (a boolean value where <b>false</b> is the
  337.       * normal, "up" state and <b>true</b> is the down or "pressed" state)
  338.       * and then calls the Button's
  339.       * <b>draw()</b> method to redraw it.  If you do not want to immediately
  340.       * redraw the Button, you should first call its
  341.       * <B>disableDrawing()</B> method.
  342.       * @see View#disableDrawing
  343.       */
  344.     public void setState(boolean newState) {
  345.         Button     otherActive;
  346.         RootView   rv = rootView();
  347.  
  348.         if (_type == RADIO_TYPE) {
  349.             if (newState == state()) {
  350.                 return;
  351.             }
  352.  
  353.             if (newState) {
  354.                 otherActive = _otherActive();
  355.                 if (otherActive != null) {
  356.                     otherActive._setState(false);
  357.                 }
  358.             }
  359.         }
  360.  
  361.         _setState(newState);
  362.     }
  363.  
  364.     /** Returns the Button's state.
  365.       * @see #setState
  366.       */
  367.     public boolean state() {
  368.         return _state;
  369.     }
  370.  
  371.     /** Sets the Button's Target, the object that will receive the a
  372.       * <b>performCommand()</b> message when the user clicks the Button.
  373.       */
  374.     public void setTarget(Target aTarget) {
  375.         _target = aTarget;
  376.     }
  377.  
  378.     /** Returns the Button's Target.
  379.       * @see #setTarget
  380.       */
  381.     public Target target() {
  382.         return _target;
  383.     }
  384.  
  385.     /** Sets the Button's command.
  386.       * @see #setTarget
  387.       */
  388.     public void setCommand(String command) {
  389.         _command = command;
  390.     }
  391.  
  392.     /** Returns the Button's command.
  393.       * @see #setCommand
  394.       */
  395.     public String command() {
  396.         return _command;
  397.     }
  398.  
  399.     /** Returns the Size required to display the Button's Images.
  400.       * @see #setImage
  401.       */
  402.     public Size imageAreaSize() {
  403.         Size    theSize;
  404.  
  405.         if (_image != null) {
  406.             theSize = new Size(_image.width(), _image.height());
  407.         } else {
  408.             theSize = new Size();
  409.         }
  410.         if (_altImage != null) {
  411.             if (_altImage.width() > theSize.width) {
  412.                 theSize.width = _altImage.width();
  413.             }
  414.             if (_altImage.height() > theSize.height) {
  415.                 theSize.height = _altImage.height();
  416.             }
  417.         }
  418.  
  419. /*        if (_upSequence != null) {
  420.             if (_upSequence.width() > theSize.width) {
  421.                 theSize.width = _upSequence.width();
  422.             }
  423.             if (_upSequence.height() > theSize.height) {
  424.                 theSize.height = _upSequence.height();
  425.             }
  426.         }
  427.  
  428.         if (_downSequence != null) {
  429.             if (_downSequence.width() > theSize.width) {
  430.                 theSize.width = _downSequence.width();
  431.             }
  432.             if (_downSequence.height() > theSize.height) {
  433.                 theSize.height = _downSequence.height();
  434.             }
  435.         }*/
  436.  
  437.         return theSize;
  438.     }
  439.  
  440.     Size minStringSize(String string) {
  441.         Size    theSize;
  442.         String  titleRemainder;
  443.         int     lineCount, newline, substringWidth;
  444.  
  445.         newline = string.indexOf('\n');
  446.         if (newline == -1) {
  447.             return _titleFont.fontMetrics().stringSize(string);
  448.         }
  449.  
  450.         theSize = new Size(0, _titleFont.fontMetrics().stringHeight());
  451.         lineCount = 1;
  452.         titleRemainder = string;
  453.         while (newline != -1) {
  454.             substringWidth = _titleFont.fontMetrics().stringWidth(
  455.                                         titleRemainder.substring(0, newline));
  456.             if (substringWidth > theSize.width) {
  457.                 theSize.width = substringWidth;
  458.             }
  459.             lineCount++;
  460.             titleRemainder = titleRemainder.substring(newline + 1);
  461.             newline = titleRemainder.indexOf('\n');
  462.         }
  463.         substringWidth = _titleFont.fontMetrics().stringWidth(titleRemainder);
  464.         if (substringWidth > theSize.width) {
  465.             theSize.width = substringWidth;
  466.         }
  467.  
  468.         theSize.height *= lineCount;
  469.  
  470.         return theSize;
  471.     }
  472.  
  473.     /** Returns the minimum Size required to fully display the Button's
  474.       * Border, title, and Images.
  475.       */
  476.     public Size minSize() {
  477.         Size            theSize = null, altSize = null, imageAreaSize;
  478.         boolean         hasTitle = false, hasImage;
  479.  
  480.         if (_minSize != null) {
  481.             return new Size(_minSize);
  482.         }
  483.  
  484.         if (_title != null && _title.length() > 0) {
  485.             theSize = minStringSize(_title);
  486.             hasTitle = true;
  487.         }
  488.         if (_altTitle != null && _altTitle.length() > 0) {
  489.             altSize = minStringSize(_altTitle);
  490.             hasTitle = true;
  491.         }
  492.  
  493.         if (theSize == null) {
  494.             theSize = altSize;
  495.         } else if (altSize != null) {
  496.             if (theSize.width < altSize.width) {
  497.                 theSize.width = altSize.width;
  498.             }
  499.             if (theSize.height < altSize.height) {
  500.                 theSize.height = altSize.height;
  501.             }
  502.         }
  503.  
  504.         if (theSize != null) {
  505.             if (theSize.width > 0) {
  506.                 theSize.sizeBy(3, 0);
  507.             }
  508.         } else {
  509.             theSize = new Size();
  510.         }
  511.  
  512.         imageAreaSize = imageAreaSize();
  513.         hasImage = (imageAreaSize.width > 0 || imageAreaSize.height > 0);
  514.  
  515.         if (hasImage) {
  516.             if (_imagePosition == IMAGE_ABOVE ||
  517.                 _imagePosition == IMAGE_BELOW) {
  518.                 if (theSize.width < imageAreaSize.width) {
  519.                     theSize.width = imageAreaSize.width;
  520.                 }
  521.                 theSize.height += imageAreaSize.height + 2;
  522.             } else if (hasTitle && _imagePosition != IMAGE_BENEATH) {
  523.                 theSize.sizeBy(imageAreaSize.width + 2, 0);
  524.  
  525.                 if (imageAreaSize.height > theSize.height) {
  526.                     theSize.height = imageAreaSize.height;
  527.                 }
  528.             } else {
  529.                 if (imageAreaSize.width > theSize.width) {
  530.                     theSize.width = imageAreaSize.width;
  531.                 }
  532.                 if (imageAreaSize.height > theSize.height) {
  533.                     theSize.height = imageAreaSize.height;
  534.                 }
  535.             }
  536.         }
  537.  
  538.         if (_bordered) {
  539.             theSize.sizeBy(3, 3);
  540.         }
  541.  
  542.         return theSize;
  543.     }
  544.  
  545.     /** Sets the Font used to display the Button's title.
  546.       * @see #setTitle
  547.       */
  548.     public void setFont(Font aFont) {
  549.         if (aFont == null) {
  550.             _titleFont = Font.defaultFont();
  551.         } else {
  552.             _titleFont = aFont;
  553.         }
  554.     }
  555.  
  556.     /** Returns the Font used to display the Button's title.
  557.       * @see #setFont
  558.       */
  559.     public Font font() {
  560.         return _titleFont;
  561.     }
  562.  
  563.     /** Sets the Color used to draw the Button's title.
  564.       * @see #setTitle
  565.       */
  566.     public void setTitleColor(Color aColor) {
  567.         if (aColor == null) {
  568.             _titleColor = Color.black;
  569.         } else {
  570.             _titleColor = aColor;
  571.         }
  572.     }
  573.  
  574.     /** Returns the Color used to draw the Button's title.
  575.       * @see #setTitleColor
  576.       */
  577.     public Color titleColor() {
  578.         return _titleColor;
  579.     }
  580.  
  581.     /** Sets the Color used to draw the Button's title when disabled.
  582.       * @see #setTitle
  583.       */
  584.     public void setDisabledTitleColor(Color aColor) {
  585.         if (aColor == null) {
  586.             _disabledTitleColor = Color.gray;
  587.         } else {
  588.             _disabledTitleColor = aColor;
  589.         }
  590.     }
  591.  
  592.     /** Returns the Color used to draw the Button's title when disabled.
  593.       * @see #setDisabledTitleColor
  594.       */
  595.     public Color disabledTitleColor() {
  596.         return _disabledTitleColor;
  597.     }
  598.  
  599.     /** Sets the background Color used to draw the Button in its raised state.
  600.       */
  601.     public void setRaisedColor(Color aColor) {
  602.         if (aColor == null) {
  603.             _raisedColor = Color.lightGray;
  604.         } else {
  605.             _raisedColor = aColor;
  606.         }
  607.     }
  608.  
  609.     /** Returns the background Color used to draw the Button in its raised
  610.       * state.
  611.       * @see #setRaisedColor
  612.       */
  613.     public Color raisedColor() {
  614.         return _raisedColor;
  615.     }
  616.  
  617.     /** Sets the background Color used to draw the Button in its lowered state.
  618.       */
  619.     public void setLoweredColor(Color aColor) {
  620.         if (aColor == null) {
  621.             _loweredColor = Color.lightGray;
  622.         } else {
  623.             _loweredColor = aColor;
  624.         }
  625.     }
  626.  
  627.     /** Returns the background Color used to draw the Button in its lowered
  628.       * state.
  629.       * @see #setLoweredColor
  630.       */
  631.     public Color loweredColor() {
  632.         return _loweredColor;
  633.     }
  634.  
  635.     /** Sets the Image displayed by the Button.  If <b>anImage</b> is a
  636.       * DrawingSequence, sets the Button to be the DrawingSequence's owner and
  637.       * starts the sequence when the user releases the Button (after a press).
  638.       * @see DrawingSequence
  639.       * @see #setAltImage
  640.       */
  641.     public void setImage(Image anImage) {
  642.         DrawingSequence         sequence;
  643.  
  644.         _image = anImage;
  645.  
  646.         if (anImage instanceof DrawingSequence) {
  647.             sequence = (DrawingSequence)anImage;
  648.             sequence.setOwner(this);
  649.         }
  650.     }
  651.  
  652.     /** Returns the Image displayed by the Button.
  653.       * @see #setImage
  654.       */
  655.     public Image image() {
  656.         return _image;
  657.     }
  658.  
  659.     /** Sets the Image the button displays when pressed. If <b>anImage</b>
  660.       * is a DrawingSequence, sets the Button to be the DrawingSequence's owner
  661.       * and starts the sequence when the user presses the Button.
  662.       * @see DrawingSequence
  663.       */
  664.     public void setAltImage(Image anImage) {
  665.         DrawingSequence         sequence;
  666.  
  667.         _altImage = anImage;
  668.  
  669.         if (anImage instanceof DrawingSequence) {
  670.             sequence = (DrawingSequence)anImage;
  671.             sequence.setOwner(this);
  672.         }
  673.     }
  674.  
  675.     /** Returns the Image the Button displays when pressed.
  676.       * @see #setAltImage
  677.       */
  678.     public Image altImage() {
  679.         return _altImage;
  680.     }
  681.  
  682.     /** Sets the raised Border, the Border the Button draws around its
  683.       * perimeter when in the "up" state.
  684.       */
  685.     public void setRaisedBorder(Border border) {
  686.         _raisedBorder = border;
  687.     }
  688.  
  689.     /** Returns the Button's raised Border.
  690.       * @see #setRaisedBorder
  691.       */
  692.     public Border raisedBorder() {
  693.         return _raisedBorder;
  694.     }
  695.  
  696.     /** Sets the lowered Border, the Border the Button draws around its
  697.       * perimeter when in the "down" state.
  698.       */
  699.     public void setLoweredBorder(Border border) {
  700.         _loweredBorder = border;
  701.     }
  702.  
  703.     /** Returns the Button's lowered Border.
  704.       * @see #setLoweredBorder
  705.       */
  706.     public Border loweredBorder() {
  707.         return _loweredBorder;
  708.     }
  709.  
  710.     /** Sets the Sound the Button plays when pressed.
  711.       */
  712.     public void setMouseDownSound(Sound aSound) {
  713.         _downSound = aSound;
  714.         if (_type == CONTINUOUS_TYPE && _downSound != null) {
  715.             _downSound.setLoops(true);
  716.         }
  717.     }
  718.  
  719.     /** Returns the Sound the Button plays when pressed.
  720.       * @see #setMouseDownSound
  721.       */
  722.     public Sound mouseDownSound() {
  723.         return _downSound;
  724.     }
  725.  
  726.     /** Sets the Sound the Button plays when released, after being pressed.
  727.       */
  728.     public void setMouseUpSound(Sound aSound) {
  729.         _upSound = aSound;
  730.     }
  731.  
  732.     /** Returns the Sound the Button plays when released.
  733.       * @see #setMouseUpSound
  734.       */
  735.     public Sound mouseUpSound() {
  736.         return _upSound;
  737.     }
  738.  
  739.     /** Sets whether the Button draws its Border.
  740.       */
  741.     public void setBordered(boolean flag) {
  742.         _bordered = flag;
  743.         if (_bordered) {
  744.             setTransparent(false);
  745.         }
  746.     }
  747.  
  748.     /** Returns <b>true</b> if the Button draws its Border.
  749.       * @see #setBordered
  750.       */
  751.     public boolean isBordered() {
  752.         return _bordered;
  753.     }
  754.  
  755. /*    public void setBackgroundCanChange(boolean flag) {
  756.         _backgroundCanChange = flag;
  757.     }
  758.  
  759.     public boolean backgroundCanChange() {
  760.         return _backgroundCanChange;
  761.     }*/
  762.  
  763.     /** Sets the Button's type, which can be one of the following:
  764.       * <OL><LI>A PUSH_TYPE Button appears to press in when clicked and sends
  765.       * its command to its Target when released, returning to its original
  766.       * "up" state.  <LI>A TOGGLE_TYPE Button
  767.       * changes its state between off and on and sends its command to its
  768.       * Target on each click.  <LI>A RADIO_TYPE button behaves like a
  769.       * TOGGLE_TYPE button, except when clicked it turns off any other
  770.       * RADIO_TYPE buttons with the same superview.
  771.       * <LI>A CONTINUOUS_TYPE Button behaves like a PUSH_TYPE
  772.       * Button, except that it sends its command to its Target while pressed,
  773.       * every <b>repeatDelay()</b> milliseconds. </OL>
  774.       * @see #setRepeatDelay
  775.       */
  776.     public void setType(int buttonType) {
  777.         if (buttonType < 0 || buttonType > CONTINUOUS_TYPE) {
  778.             throw new InconsistencyException(
  779.                                     "Invalid Button type: " + buttonType);
  780.         }
  781.  
  782.         _type = buttonType;
  783.  
  784.         setState(false);
  785.         if (_type == CONTINUOUS_TYPE) {
  786.             if (_downSound != null) {
  787.                 _downSound.setLoops(true);
  788.             }
  789.         } else {
  790.             if (_downSound != null) {
  791.                 _downSound.setLoops(false);
  792.             }
  793.         }
  794.         _setupKeyboard();
  795.     }
  796.  
  797.     /** Returns the Button's type.
  798.       * @see #setType
  799.       */
  800.     public int type() {
  801.         return _type;
  802.     }
  803.  
  804.     /** Sets the Button's repeat delay, the time delay a CONTINUOUS_TYPE Button
  805.       * uses when sending its command to its Target while being pressed.
  806.       * @see #setType
  807.       */
  808.     public void setRepeatDelay(int milliseconds) {
  809.         if (milliseconds > 0) {
  810.             _repeatDelay = milliseconds;
  811.             if (_actionTimer != null) {
  812.                 _actionTimer.setDelay(_repeatDelay);
  813.             }
  814.         }
  815.     }
  816.  
  817.     /** Returns the Button's repeat delay.
  818.       * @see #setRepeatDelay
  819.       */
  820.     public int repeatDelay() {
  821.         return _repeatDelay;
  822.     }
  823.  
  824.     /** Sets the relationship between the Button's Images and its title.
  825.       * Options are IMAGE_ON_LEFT, IMAGE_ON_RIGHT, IMAGE_ABOVE,
  826.       * IMAGE_BELOW, and IMAGE_BENEATH.
  827.       * @see #setImage
  828.       * @see #setTitle
  829.       */
  830.     public void setImagePosition(int aPosition) {
  831.         if (aPosition < 0 || aPosition > IMAGE_BENEATH) {
  832.             return;
  833.         }
  834.         _imagePosition = aPosition;
  835.     }
  836.  
  837.     /** Returns the integer describing the relationship between the Button's
  838.       * Images and title.
  839.       * @see #setImagePosition
  840.       */
  841.     public int imagePosition() {
  842.         return _imagePosition;
  843.     }
  844.  
  845.     /** Overridden to configure the Button not to draw its Border, if
  846.       * configured to be transparent.  A transparent Button does not display a
  847.       * Border (unless subsequently configured to do so) or a background color,
  848.       * only its title and Images, if any.
  849.       */
  850.     public void setTransparent(boolean flag) {
  851.         transparent = flag;
  852.         if (transparent) {
  853.             _bordered = false;
  854.         }
  855.     }
  856.  
  857.     /** Overridden to return <b>true</b> if the Button is transparent.
  858.       * @see #setTransparent
  859.       */
  860.     public boolean isTransparent() {
  861.         return transparent;
  862.     }
  863.  
  864.     /** Overridden to support RADIO_TYPE buttons.
  865.       */
  866.     protected void ancestorWasAddedToViewHierarchy(View aView) {
  867.         super.ancestorWasAddedToViewHierarchy(aView);
  868.  
  869.         if ((_type == RADIO_TYPE) && _state) {
  870.             _state = false;
  871.             setState(true);
  872.         }
  873.     }
  874.  
  875.  
  876. /* drawing */
  877.  
  878.     /** Sets the appropriate Color (according to whether or not the Button's
  879.       * enabled) and calls Graphics' <b>drawStringInRect()</b> to draw the
  880.       * Button's title within <b>textBounds</b> using the justification
  881.       * <b>justification</b>.  You never call this method directly, but should
  882.       * override to produce any custom title drawing.
  883.       */
  884.     public void drawViewTitleInRect(Graphics g, String title,
  885.                                     Font titleFont, Rect textBounds,
  886.                                     int justification) {
  887.         Rect    tmpRect;
  888.         String  titleRemainder;
  889.         int     newline;
  890.  
  891.         if (title == null || title.length() == 0) {
  892.             return;
  893.         }
  894.  
  895.         if (_enabled) {
  896.             g.setColor(_titleColor);
  897.         } else {
  898.             g.setColor(_disabledTitleColor);
  899.         }
  900.  
  901.         g.setFont(titleFont);
  902. //      g.drawStringInRect(title, textBounds, justification);
  903.  
  904.         /* this code allows Buttons to draw multiple lines, separated by
  905.          * newlines
  906.          */
  907.         newline = title.indexOf('\n');
  908.         if (newline == -1) {
  909.             g.drawStringInRect(title, textBounds, justification);
  910.             return;
  911.         }
  912.  
  913.         tmpRect = new Rect(textBounds);
  914.         tmpRect.height = _titleFont.fontMetrics().stringHeight();
  915.         titleRemainder = title;
  916.         while (newline != -1) {
  917.             g.drawStringInRect(titleRemainder.substring(0, newline),
  918.                                tmpRect, justification);
  919.             tmpRect.y += tmpRect.height;
  920.             titleRemainder = titleRemainder.substring(newline + 1);
  921.             newline = titleRemainder.indexOf('\n');
  922.         }
  923.         g.drawStringInRect(titleRemainder, tmpRect, justification);
  924.     }
  925.  
  926.     /** Draws the Button's interior. <b>title</b> is the appropriate String
  927.       * and <b>image</b> is the appropriate Image to be drawn in the Button's
  928.       * current state. <b>interiorRect</b> is the Rect defining the Button's
  929.       * interior (the entire Button without its border). Calls
  930.       * <b>drawViewTitleInRect()</b> to draw the Button's title.  You never
  931.       * call this method directly, but should override to implement any custom
  932.       * drawing.
  933.       */
  934.     public void drawViewInterior(Graphics g, String title, Image image,
  935.                                  Rect interiorRect) {
  936.         Size            imageAreaSize, stringSize;
  937.         int             x, charHeight, justification;
  938.         boolean         center;
  939.  
  940.         imageAreaSize = imageAreaSize();
  941.  
  942.         if (_imagePosition == IMAGE_ON_LEFT) {
  943.             if (title == null || title.length() == 0) {
  944.                 x = interiorRect.x + 1 +
  945.                     (interiorRect.width - imageAreaSize.width - 2) / 2;
  946.             } else {
  947.                 x = interiorRect.x + 1;
  948.             }
  949.  
  950.             if (image != null) {
  951.                 image.drawAt(g, x, interiorRect.y + (interiorRect.height -
  952.                     imageAreaSize.height) / 2);
  953.             }
  954.  
  955.             /* put some distance between the image and the text */
  956.             if (imageAreaSize.width > 0) {
  957.                 interiorRect.moveBy(imageAreaSize.width + 3, 0);
  958.                 interiorRect.sizeBy(-(imageAreaSize.width + 4), 0);
  959.                 justification = Graphics.LEFT_JUSTIFIED;
  960.             } else {
  961.                 interiorRect.moveBy(1, 0);
  962.                 interiorRect.sizeBy(-2, 0);
  963.                 justification = Graphics.CENTERED;
  964.             }
  965.  
  966.             drawViewTitleInRect(g, title, _titleFont, interiorRect,
  967.                                 justification);
  968.         } else if (_imagePosition == IMAGE_ABOVE) {
  969.             if (image != null) {
  970.                 image.drawAt(g, interiorRect.x + (interiorRect.width -
  971.                     imageAreaSize.width) / 2, interiorRect.y + 2);
  972.             }
  973.  
  974.             charHeight = _titleFont.fontMetrics().charHeight();
  975.             interiorRect.setBounds(interiorRect.x + 1,
  976.                                    interiorRect.maxY() - charHeight - 1,
  977.                                    interiorRect.width - 2, charHeight);
  978.             drawViewTitleInRect(g, title, _titleFont, interiorRect,
  979.                                 Graphics.CENTERED);
  980.         } else if (_imagePosition == IMAGE_BELOW) {
  981.             if (image != null) {
  982.                 image.drawAt(g, interiorRect.x + (interiorRect.width -
  983.                     imageAreaSize.width) / 2,  interiorRect.maxY() -
  984.                     imageAreaSize.height - 2);
  985.             }
  986.  
  987.             interiorRect.setBounds(interiorRect.x + 1, interiorRect.y + 1,
  988.                                    interiorRect.width - 2,
  989.                                    _titleFont.fontMetrics().charHeight());
  990.             drawViewTitleInRect(g, title, _titleFont, interiorRect,
  991.                                 Graphics.CENTERED);
  992.         } else {
  993.             if (image != null && _imagePosition == IMAGE_BENEATH) {
  994.                 image.drawAt(g, interiorRect.x +
  995.                                 (interiorRect.width - imageAreaSize.width) / 2,
  996.                              interiorRect.y +
  997.                                (interiorRect.height - imageAreaSize.height)/2);
  998.                 justification = Graphics.CENTERED;
  999.             } else if (imageAreaSize.width == 0) {
  1000.                 interiorRect.moveBy(2, 0);
  1001.                 justification = Graphics.CENTERED;
  1002.             } else {
  1003.                 justification = Graphics.LEFT_JUSTIFIED;
  1004.             }
  1005.  
  1006.             if (title == null || title.length() == 0) {
  1007.                 x = interiorRect.x + 1 +
  1008.                     (interiorRect.width - imageAreaSize.width - 2) / 2;
  1009.             } else {
  1010.                 x = interiorRect.maxX() - imageAreaSize.width - 1;
  1011.             }
  1012.  
  1013.             if (image != null && _imagePosition == IMAGE_ON_RIGHT) {
  1014.                 image.drawAt(g, x, interiorRect.y + (interiorRect.height -
  1015.                     imageAreaSize.height) / 2);
  1016.             }
  1017.  
  1018.             drawViewTitleInRect(g, title, _titleFont, interiorRect,
  1019.                                 justification);
  1020.         }
  1021.     }
  1022.  
  1023.     /** Draws the Button's background (a Border, or solid rectangle if no
  1024.       * Border and opaque).  You never call this method directly, but should
  1025.       * override to perform any custom background drawing.  You must modify
  1026.       * <b>interiorRect</b> to define the
  1027.       * area available to draw the Button's title and/or Image.
  1028.       */
  1029.     public void drawViewBackground(Graphics g, Rect interiorRect,
  1030.                                    boolean drawDownState) {
  1031.         if (_bordered) {
  1032.             interiorRect.sizeBy(-3, -3);
  1033.             if (drawDownState) {
  1034.                 _loweredBorder.drawInRect(g, 0, 0, bounds.width,
  1035.                                           bounds.height);
  1036.                 g.setColor(_loweredColor);
  1037.                 g.fillRect(_loweredBorder.leftMargin(),
  1038.                            _loweredBorder.topMargin(),
  1039.                            bounds.width - _loweredBorder.widthMargin(),
  1040.                            bounds.height - _loweredBorder.heightMargin());
  1041.                 interiorRect.moveBy(2, 2);
  1042.             } else {
  1043.                 _raisedBorder.drawInRect(g, 0, 0, bounds.width,
  1044.                                          bounds.height);
  1045.                 g.setColor(_raisedColor);
  1046.                 g.fillRect(_raisedBorder.leftMargin(),
  1047.                            _raisedBorder.topMargin(),
  1048.                            bounds.width - _raisedBorder.widthMargin(),
  1049.                            bounds.height - _raisedBorder.heightMargin());
  1050.                 interiorRect.moveBy(1, 1);
  1051.             }
  1052.         } else {
  1053.             if (!isTransparent()) {
  1054.                 if (drawDownState) {
  1055.                     g.setColor(_loweredColor);
  1056.                 } else {
  1057.                     g.setColor(_raisedColor);
  1058.                 }
  1059.                 g.fillRect(0, 0, bounds.width, bounds.height);
  1060.             }
  1061.         }
  1062.     }
  1063.  
  1064.     /** Draws the Button.  Calls <b>drawViewBackground()</b> and
  1065.       * <b>drawViewInterior()</b>.  You never call this method directly, but
  1066.       * may override to implement custom Button drawing.  Use the <b>draw()</b>
  1067.       * method to draw the Button.
  1068.       * @see View#draw
  1069.       */
  1070.     public void drawView(Graphics g) {
  1071.         Image                   theImage = null;
  1072.         DrawingSequence         sequence;
  1073.         String                  theTitle;
  1074.         Rect                    interiorRect;
  1075.         boolean                 drawDownState;
  1076.  
  1077.         drawDownState = _highlighted ? !_state : _state;
  1078.         //        if ((_image != null /*|| _upSequence != null*/) &&
  1079.         //           (_altImage != null /*|| _downSequence != null*/)) {
  1080.         //           backgroundCanChange = false;
  1081.         //        } else {
  1082.         //            backgroundCanChange = true;
  1083.         //        }
  1084.  
  1085.         /* bezel */
  1086.         interiorRect = Rect.newRect(0, 0, bounds.width, bounds.height);
  1087.         drawViewBackground(g, interiorRect, drawDownState);
  1088.  
  1089.         /* which image should appear - first, should be any running sequence */
  1090.         if (_image instanceof DrawingSequence) {
  1091.             sequence = (DrawingSequence)_image;
  1092.             if (sequence.isAnimating()) {
  1093.                 theImage = _image;
  1094.             } else if (_altImage instanceof DrawingSequence) {
  1095.                 /* trust me - this is what you want */
  1096.                 theImage = _altImage;
  1097.             }
  1098.         } else if (_altImage instanceof DrawingSequence) {
  1099.             sequence = (DrawingSequence)_altImage;
  1100.             if (sequence.isAnimating()) {
  1101.                 theImage = _altImage;
  1102.             }
  1103.         }
  1104.  
  1105.         /* no sequences, or running sequences, select an image */
  1106.         if (theImage == null) {
  1107.             theImage = _image;
  1108.             if (drawDownState && _altImage != null) {
  1109.                 theImage = _altImage;
  1110.             }
  1111.         }
  1112.  
  1113. /*        if (_upSequence != null && _upSequence.isAnimating()) {
  1114.             theSequence = _upSequence;
  1115.         } else {
  1116.             theSequence = _downSequence;
  1117.         }*/
  1118.  
  1119.         if (drawDownState && _altTitle != null && _altTitle.length() != 0) {
  1120.             theTitle = _altTitle;
  1121.         } else {
  1122.             theTitle = _title;
  1123.         }
  1124.  
  1125.         /* nothing more to draw? */
  1126.         if (theImage == null &&
  1127.             (theTitle == null || theTitle.length() == 0)) {
  1128.             Rect.returnRect(interiorRect);
  1129.             return;
  1130.         }
  1131.  
  1132. //        if (theSequence != null) {
  1133. //            drawViewInterior(g, theSequence, interiorRect);
  1134. //        } else {
  1135.             drawViewInterior(g, theTitle, theImage, interiorRect);
  1136. //        }
  1137.  
  1138.         Rect.returnRect(interiorRect);
  1139.     }
  1140.  
  1141.  
  1142.   /* events */
  1143.  
  1144.     Button _activeForPoint(int x, int y) {
  1145.         Button          nextPeer;
  1146.         View            nextView;
  1147.         Point           point;
  1148.         Vector          peers;
  1149.         int             i;
  1150.  
  1151.         if (superview() == null) {
  1152.             return null;
  1153.         }
  1154.  
  1155.         peers = superview().peersForSubview(this);
  1156.  
  1157.         point = Point.newPoint();
  1158.         i = peers.count();
  1159.         while (i-- > 0) {
  1160.             nextView = (View)peers.elementAt(i);
  1161.  
  1162.             if (!(nextView instanceof Button) || nextView == this) {
  1163.                 continue;
  1164.             }
  1165.  
  1166.             nextPeer = (Button)nextView;
  1167.             if (nextPeer.type() != RADIO_TYPE) {
  1168.                 continue;
  1169.             }
  1170.  
  1171.             convertToView(nextView, x, y, point);
  1172.  
  1173.             if (nextPeer.containsPoint(point.x, point.y)) {
  1174.                 Point.returnPoint(point);
  1175.                 return nextPeer;
  1176.             }
  1177.         }
  1178.  
  1179.         Point.returnPoint(point);
  1180.  
  1181.         return null;
  1182.     }
  1183.  
  1184.     /** Overridden to interpret Button clicks.  For CONTINUOUS_TYPE
  1185.       * Buttons, starts a Timer that sends the Button's command to its
  1186.       * Target every <b>repeatDelay()</b> milliseconds.
  1187.       * @see #setRepeatDelay
  1188.       */
  1189.     public boolean mouseDown(MouseEvent event) {
  1190.         Button                  otherActive;
  1191.         DrawingSequence         sequence;
  1192.  
  1193.         if (!_enabled) {
  1194.             return false;
  1195.         }
  1196.  
  1197.         if (!containsPoint(event.x, event.y)) {
  1198.             return false;
  1199.         }
  1200.  
  1201.         if (_type == RADIO_TYPE) {
  1202.             otherActive = _otherActive();
  1203.             if (otherActive != null) {
  1204.                 otherActive._setState(false);
  1205.             }
  1206.             _oldState = _state;
  1207.             _state = false;
  1208.         }
  1209.  
  1210.         _clickCount = event.clickCount;
  1211.  
  1212.         if (_type == TOGGLE_TYPE || _type == RADIO_TYPE)
  1213.             setHighlighted(true);
  1214.         else
  1215.             setState(true);
  1216.  
  1217.         if (_downSound != null) {
  1218.             _downSound.play();
  1219.         }
  1220.  
  1221.         if (_altImage instanceof DrawingSequence) {
  1222.             sequence = (DrawingSequence)_altImage;
  1223.             sequence.start();
  1224.         }
  1225.  
  1226.         if (_type == CONTINUOUS_TYPE && _actionTimer == null) {
  1227.             sendCommand();
  1228. // ALERT! - Buttons shouldn't set the initial delay behind your back!
  1229.             _actionTimer = new Timer(this, "sendCommand", _repeatDelay);
  1230.             _actionTimer.setInitialDelay(300);
  1231.             _actionTimer.start();
  1232.         }
  1233.  
  1234.         return true;
  1235.     }
  1236.  
  1237.     void _buttonDown() {
  1238.         DrawingSequence         sequence;
  1239.  
  1240.         if (_image instanceof DrawingSequence) {
  1241.             sequence = (DrawingSequence)_image;
  1242.             if (sequence.doesLoop()) {
  1243.                 sequence.stop();
  1244.             } else {
  1245.                /* wait for it to finish */
  1246.                 while (sequence.isAnimating()) {
  1247. // ALERT!!!
  1248. //                  Thread.yield();
  1249. //                  Application.sleep(5);
  1250.                     sequence.stop();
  1251.                 }
  1252.             }
  1253.         }
  1254.  
  1255.         if (_altImage instanceof DrawingSequence) {
  1256.             sequence = (DrawingSequence)_altImage;
  1257.             sequence.start();
  1258.         }
  1259.  
  1260.         if (_type == CONTINUOUS_TYPE) {
  1261.             if (_upSound != null) {
  1262.                 _upSound.stop();
  1263.             }
  1264.         }
  1265.         if (_downSound != null) {
  1266.             _downSound.play();
  1267.         }
  1268.     }
  1269.  
  1270.     void _buttonUp() {
  1271.         DrawingSequence         sequence;
  1272.  
  1273.         if (_altImage instanceof DrawingSequence) {
  1274.             sequence = (DrawingSequence)_altImage;
  1275.             if (sequence.doesLoop()) {
  1276.                 sequence.stop();
  1277.             } else {
  1278.                /* wait for it to finish */
  1279.                 while (sequence.isAnimating()) {
  1280. // ALERT!!!
  1281. //                  Thread.yield();
  1282. //                  Application.sleep(5);
  1283.                     sequence.stop();
  1284.                 }
  1285.             }
  1286.         }
  1287.  
  1288.         if (_image instanceof DrawingSequence) {
  1289.             sequence = (DrawingSequence)_image;
  1290.             sequence.start();
  1291.         }
  1292.  
  1293.         if (_type == CONTINUOUS_TYPE) {
  1294.             if (_downSound != null) {
  1295.                 _downSound.stop();
  1296.             }
  1297.         }
  1298.         if (_upSound != null) {
  1299.             _upSound.play();
  1300.         }
  1301.     }
  1302.  
  1303.     /** Overridden to support Button clicks. */
  1304.     public void mouseDragged(MouseEvent event) {
  1305.         Button          newButton;
  1306.         boolean         newState;
  1307.  
  1308.         if (!_enabled) {
  1309.             return;
  1310.         }
  1311.  
  1312.         if (_type == RADIO_TYPE && !containsPoint(event.x, event.y)) {
  1313.             newButton = _activeForPoint(event.x, event.y);
  1314.             if (newButton != null) {
  1315.                 setHighlighted(false);
  1316.                 newButton.setHighlighted(true);
  1317.  
  1318.                 rootView().setMouseView(newButton);
  1319.             }
  1320.             return;
  1321.         }
  1322.  
  1323.       /* did the user move the mouse away from the button */
  1324.         if (containsPoint(event.x, event.y)) {
  1325.             if (!(_state || _highlighted)) {
  1326.                 _buttonDown();
  1327.                 if (_type == TOGGLE_TYPE || _type == RADIO_TYPE)
  1328.                     setHighlighted(true);
  1329.                 else
  1330.                     setState(true);
  1331.                 if (_type == CONTINUOUS_TYPE) {
  1332.                     sendCommand();
  1333.                     _actionTimer = new Timer(this, "sendCommand", 100);
  1334.                     _actionTimer.start();
  1335.                 }
  1336.             }
  1337.         } else {
  1338.             if (_state || _highlighted) {
  1339.                 _buttonUp();
  1340.                 if (_type == CONTINUOUS_TYPE) {
  1341.                     if (_actionTimer != null) {
  1342.                         _actionTimer.stop();
  1343.                         _actionTimer = null;
  1344.                     }
  1345.                 }
  1346.                 if (_type == TOGGLE_TYPE || _type == RADIO_TYPE)
  1347.                     setHighlighted(false);
  1348.                 else
  1349.                     setState(false);
  1350.             }
  1351.         }
  1352.     }
  1353.  
  1354.     /** Overridden to support Button clicks. */
  1355.     public void mouseUp(MouseEvent event) {
  1356.         boolean         mouseOverButton;
  1357.  
  1358.         if (!_enabled) {
  1359.             return;
  1360.         }
  1361.  
  1362.         if (_type == RADIO_TYPE) {
  1363.             if (_highlighted) {
  1364.                 _highlighted = false;
  1365.                 setState(true);
  1366.             }
  1367.  
  1368.             if (_state != _oldState) {
  1369.                 sendCommand();
  1370.             }
  1371.             _oldState = false;
  1372.  
  1373.             if(canBecomeSelectedView() && rootView() != null)
  1374.                 rootView().selectView(this,true);
  1375.  
  1376.             return;
  1377.         }
  1378.  
  1379.         // First of all stop the timed event
  1380.         if (_actionTimer != null) {
  1381.             _actionTimer.stop();
  1382.             _actionTimer = null;
  1383.         }
  1384.  
  1385.         // If we're still in the rect raise the button
  1386.         mouseOverButton = containsPoint(event.x, event.y);
  1387.         if (mouseOverButton) {
  1388.             _buttonUp();
  1389.         }
  1390.  
  1391.         if (_type == CONTINUOUS_TYPE) {
  1392.             _state = false;
  1393.             if (mouseOverButton) {
  1394.                 setDirty(true);
  1395.             }
  1396.             if(canBecomeSelectedView() && rootView() != null)
  1397.                 rootView().selectView(this,true);
  1398.             return;
  1399.         }
  1400.  
  1401.         // Only toggle buttons retain their state
  1402.         if (_type == TOGGLE_TYPE) {
  1403.             if (mouseOverButton) {
  1404.                 _highlighted = false;
  1405.                 _state = !_state;
  1406.             } else
  1407.                 _highlighted = false;
  1408.  
  1409.             if (mouseOverButton)
  1410.                 setDirty(true);
  1411.  
  1412.             if (mouseOverButton) {
  1413.                 sendCommand();
  1414.             }
  1415.         } else { /* Simple push button change the state after the action */
  1416.             if (_type != CONTINUOUS_TYPE && mouseOverButton) {
  1417.                 sendCommand();
  1418.             }
  1419.             _state = false;
  1420.             if (mouseOverButton)
  1421.                setDirty(true);
  1422.         }
  1423.         if(canBecomeSelectedView() && rootView() != null)
  1424.             rootView().selectView(this,true);
  1425.     }
  1426.  
  1427.     /** If the Button is currently pressed, returns the current mouse click
  1428.       * count (single-click, double-click, and so on).
  1429.       */
  1430.     public int clickCount() {
  1431.         if (_performingAction)
  1432.             return _clickCount;
  1433.         else
  1434.             return 0;
  1435.     }
  1436.  
  1437.     /** Presses the Button, as if the user clicked it with the mouse. */
  1438.     public void click() {
  1439.         if (!_enabled) {
  1440.             return;
  1441.         }
  1442.  
  1443.         // If we are already ON and we are a radio, do nothing
  1444.         if(_type == RADIO_TYPE && _state)   {
  1445.             return;
  1446.         }
  1447.  
  1448.         if (_type == TOGGLE_TYPE || _type == RADIO_TYPE) {
  1449.             setState(!_state);
  1450.             application().syncGraphics();
  1451.  
  1452.             sendCommand();
  1453.         } else {
  1454.             setState(true);
  1455.             application().syncGraphics();
  1456.  
  1457.             try {
  1458.                 Thread.sleep(200);
  1459.             } catch (InterruptedException e) {
  1460.             }
  1461.  
  1462.             _clickCount = 1;
  1463.             sendCommand();
  1464.  
  1465.             setState(false);
  1466.         }
  1467.     }
  1468.  
  1469.     /** Implements the Button's commands:
  1470.       * <ul>
  1471.       * <li>SEND_COMMAND - calls the Button's <b>sendCommand()</b> method,
  1472.       * causing the Button to send its command to its Target.
  1473.       * <li>CLICK - Calls the Button's <b>click()</b> method, causing the
  1474.       * Button to change its state (based on its type), redraw, and send its
  1475.       * command to its Target.
  1476.       * </ul>
  1477.       */
  1478.     public void performCommand(String command, Object data) {
  1479.         int type = type();
  1480.  
  1481.         if (SEND_COMMAND.equals(command))
  1482.             sendCommand();
  1483.         else if (CLICK.equals(command))
  1484.             click();
  1485.         else if(type == RADIO_TYPE && SELECT_PREVIOUS_RADIO_BUTTON.equals(command))
  1486.                 selectNextRadioButton(false);
  1487.         else if(type == RADIO_TYPE && SELECT_NEXT_RADIO_BUTTON.equals(command))
  1488.                 selectNextRadioButton(true);
  1489.         else
  1490.             throw new NoSuchMethodError("unknown command: " + command);
  1491.     }
  1492.  
  1493.     /** DrawingSequenceOwner method implemented to handle DrawingSequences
  1494.       * assigned to the Button as Images.
  1495.       * @see DrawingSequenceOwner#drawingSequenceFrameChanged
  1496.       */
  1497.     public void drawingSequenceFrameChanged(DrawingSequence aSequence) {
  1498.         setDirty(true);
  1499.     }
  1500.  
  1501.     /** DrawingSequenceOwner method implemented to handle DrawingSequences
  1502.       * assigned to the Button as Images.
  1503.       * @see DrawingSequenceOwner#drawingSequenceCompleted
  1504.       */
  1505.     public void drawingSequenceCompleted(DrawingSequence aSequence) {
  1506.     }
  1507.  
  1508.     /** Called by the Button to send its command to its Target.
  1509.       * @see #setTarget
  1510.       */
  1511.     public void sendCommand() {
  1512.         _performingAction = true;
  1513.  
  1514.         if (_target != null) {
  1515.             _target.performCommand(_command, this);
  1516.         }
  1517.  
  1518.         _performingAction = false;
  1519.     }
  1520.  
  1521.     /** Sets the Button to draw itself highlighted.
  1522.       * @see isHighlighted
  1523.       *
  1524.       */
  1525.     protected void setHighlighted(boolean highlighted) {
  1526.         if (_highlighted != highlighted) {
  1527.             _highlighted = highlighted;
  1528.             setDirty(true);
  1529.         }
  1530.     }
  1531.  
  1532.     /** Returns whether or not the Button has received a MOUSE_DOWN MouseEvent
  1533.       * but has yet to receive a MOUSE_UP.
  1534.       *
  1535.       */
  1536.     protected boolean isHighlighted() {
  1537.         return _highlighted;
  1538.     }
  1539.  
  1540.     /** Return whether this view can become the selected view
  1541.       * when the user is moving from view to views with the keyboard
  1542.       * Button implementation returns true for a regular button and
  1543.       * false for unselected radio buttons
  1544.       *
  1545.       */
  1546.     public boolean canBecomeSelectedView() {
  1547.         if(isEnabled() && hasKeyboardBindings()) {
  1548.             if(type() == RADIO_TYPE) {
  1549.                 if(state() == true)
  1550.                     return true;
  1551.                 else
  1552.                     return false;
  1553.             } else
  1554.                 return true;
  1555.         } else
  1556.             return false;
  1557.     }
  1558.  
  1559. /* archiving */
  1560.  
  1561.  
  1562.     /** Describes the Button class' information.
  1563.       * @see Codable#describeClassInfo
  1564.       */
  1565.     public void describeClassInfo(ClassInfo info) {
  1566.         super.describeClassInfo(info);
  1567.  
  1568.         info.addClass("netscape.application.Button", 2);
  1569.         info.addField(TITLE_KEY, STRING_TYPE);
  1570.         info.addField(ALT_TITLE_KEY, STRING_TYPE);
  1571.         info.addField(TITLE_FONT_KEY, OBJECT_TYPE);
  1572.         info.addField(TITLE_COLOR_KEY, OBJECT_TYPE);
  1573.         info.addField(DISABLED_TITLE_COLOR_KEY, OBJECT_TYPE);
  1574.         info.addField(RAISED_COLOR_KEY, OBJECT_TYPE);
  1575.         info.addField(LOWERED_COLOR_KEY, OBJECT_TYPE);
  1576.  
  1577.         info.addField(IMAGE_KEY, OBJECT_TYPE);
  1578.         info.addField(ALT_IMAGE_KEY, OBJECT_TYPE);
  1579.  
  1580.         info.addField(DOWN_SOUND_KEY, OBJECT_TYPE);
  1581.         info.addField(UP_SOUND_KEY, OBJECT_TYPE);
  1582.  
  1583.         info.addField(TARGET_KEY, OBJECT_TYPE);
  1584.         info.addField(COMMAND_KEY, STRING_TYPE);
  1585.  
  1586.         info.addField(TYPE_KEY, INT_TYPE);
  1587.         info.addField(IMAGE_POSITION_KEY, INT_TYPE);
  1588.         info.addField(REPEAT_DELAY_KEY, INT_TYPE);
  1589.  
  1590.         info.addField(STATE_KEY, BOOLEAN_TYPE);
  1591.         info.addField(ENABLED_KEY, BOOLEAN_TYPE);
  1592.         info.addField(BORDERED_KEY, BOOLEAN_TYPE);
  1593.         info.addField(TRANSPARENT_KEY, BOOLEAN_TYPE);
  1594.  
  1595.         info.addField(RAISED_BORDER_KEY, OBJECT_TYPE);
  1596.         info.addField(LOWERED_BORDER_KEY, OBJECT_TYPE);
  1597.     }
  1598.  
  1599.     /** Encodes the Button instance.
  1600.      * @see Codable#encode
  1601.      */
  1602.     public void encode(Encoder encoder) throws CodingException {
  1603.         super.encode(encoder);
  1604.  
  1605.         encoder.encodeString(TITLE_KEY, _title);
  1606.         encoder.encodeString(ALT_TITLE_KEY, _altTitle);
  1607.         encoder.encodeObject(TITLE_FONT_KEY, _titleFont);
  1608.  
  1609.         encoder.encodeObject(TITLE_COLOR_KEY, _titleColor);
  1610.         encoder.encodeObject(DISABLED_TITLE_COLOR_KEY, _disabledTitleColor);
  1611.  
  1612.         encoder.encodeObject(RAISED_COLOR_KEY, _raisedColor);
  1613.         encoder.encodeObject(LOWERED_COLOR_KEY, _loweredColor);
  1614.  
  1615.         encoder.encodeObject(IMAGE_KEY, _image);
  1616.         encoder.encodeObject(ALT_IMAGE_KEY, _altImage);
  1617.  
  1618.         encoder.encodeObject(TARGET_KEY, (Codable)_target);
  1619.         encoder.encodeString(COMMAND_KEY, _command);
  1620.  
  1621.         encoder.encodeInt(TYPE_KEY, _type);
  1622.         encoder.encodeInt(IMAGE_POSITION_KEY, _imagePosition);
  1623.         encoder.encodeInt(REPEAT_DELAY_KEY, _repeatDelay);
  1624.  
  1625.         encoder.encodeBoolean(STATE_KEY, _state);
  1626.         encoder.encodeBoolean(ENABLED_KEY, _enabled);
  1627.         encoder.encodeBoolean(BORDERED_KEY, _bordered);
  1628.         encoder.encodeBoolean(TRANSPARENT_KEY, transparent);
  1629.  
  1630.         encoder.encodeObject(RAISED_BORDER_KEY, _raisedBorder);
  1631.         encoder.encodeObject(LOWERED_BORDER_KEY, _loweredBorder);
  1632.     }
  1633.  
  1634.     /** Decodes the Button instance.
  1635.      * @see Codable#encode
  1636.      */
  1637.     public void decode(Decoder decoder) throws CodingException {
  1638.         super.decode(decoder);
  1639.  
  1640.         _title = decoder.decodeString(TITLE_KEY);
  1641.         _altTitle = decoder.decodeString(ALT_TITLE_KEY);
  1642.         _titleFont = (Font)decoder.decodeObject(TITLE_FONT_KEY);
  1643.  
  1644.         _titleColor = (Color)decoder.decodeObject(TITLE_COLOR_KEY);
  1645.         _disabledTitleColor =
  1646.             (Color)decoder.decodeObject(DISABLED_TITLE_COLOR_KEY);
  1647.  
  1648.         _raisedColor = (Color)decoder.decodeObject(RAISED_COLOR_KEY);
  1649.         _loweredColor = (Color)decoder.decodeObject(LOWERED_COLOR_KEY);
  1650.  
  1651.         _image = (Image)decoder.decodeObject(IMAGE_KEY);
  1652.         _altImage = (Image)decoder.decodeObject(ALT_IMAGE_KEY);
  1653.  
  1654.         _target = (Target)decoder.decodeObject(TARGET_KEY);
  1655.         _command = decoder.decodeString(COMMAND_KEY);
  1656.  
  1657.         _type = decoder.decodeInt(TYPE_KEY);
  1658.         _imagePosition = decoder.decodeInt(IMAGE_POSITION_KEY);
  1659.         _repeatDelay = decoder.decodeInt(REPEAT_DELAY_KEY);
  1660.  
  1661.         _state = decoder.decodeBoolean(STATE_KEY);
  1662.         _enabled = decoder.decodeBoolean(ENABLED_KEY);
  1663.         _bordered = decoder.decodeBoolean(BORDERED_KEY);
  1664.         transparent = decoder.decodeBoolean(TRANSPARENT_KEY);
  1665.  
  1666.         if (decoder.versionForClassName("netscape.application.Button") > 1) {
  1667.             _raisedBorder = (Border)decoder.decodeObject(RAISED_BORDER_KEY);
  1668.             _loweredBorder = (Border)decoder.decodeObject(LOWERED_BORDER_KEY);
  1669.         }
  1670.     }
  1671.  
  1672.     void _setupKeyboard() {
  1673.         removeAllCommandsForKeys();
  1674.         if(type() == RADIO_TYPE) {
  1675.             setCommandForKey(SELECT_NEXT_RADIO_BUTTON,KeyEvent.RIGHT_ARROW_KEY,View.WHEN_SELECTED);
  1676.             setCommandForKey(SELECT_NEXT_RADIO_BUTTON,KeyEvent.DOWN_ARROW_KEY,View.WHEN_SELECTED);
  1677.             setCommandForKey(SELECT_PREVIOUS_RADIO_BUTTON,KeyEvent.LEFT_ARROW_KEY,View.WHEN_SELECTED);
  1678.             setCommandForKey(SELECT_PREVIOUS_RADIO_BUTTON,KeyEvent.UP_ARROW_KEY,View.WHEN_SELECTED);
  1679.         } else {
  1680.             setCommandForKey(CLICK,KeyEvent.RETURN_KEY,View.WHEN_SELECTED);
  1681.         }
  1682.     }
  1683.  
  1684.     /** Implementation of the FormElement interface
  1685.       * If the button type is TOGGLE_TYPE this method
  1686.       * will returns "true" or "false" depending on it's
  1687.       * state. Otherwise, it will return it's title()
  1688.       *
  1689.       */
  1690.     public String formElementText() {
  1691.         if (_type == TOGGLE_TYPE) {
  1692.             if (_state) {
  1693.                 return "true";
  1694.             } else {
  1695.                 return "false";
  1696.             }
  1697.         } else {
  1698.             return title();
  1699.         }
  1700.     }
  1701. }
  1702.